﻿#include "GraphicsBaseItem.h"
#include <MoveCommand.h>
#include <ScaleCommand.h>
#include <RotateCommand.h>
#include <StretchCommand.h>
#include <GraphicsScene.h>
#include <GraphicsStretchOperation.h>
#include <GraphicsRotateOperation.h>
#include <GraphicsMoveOperation.h>
#include <GraphicsScaleOperation.h>
#include <QGraphicsView>
#include <QGraphicsSceneMouseEvent>

GraphicsBaseItem::GraphicsBaseItem(GraphicsItemType baseItemType, GraphicsScene* scene, GraphicsBaseItem* parent)
	: QGraphicsObject(parent)
	, m_scene(scene)
	, m_data(GraphicsBaseItemData(baseItemType, parent))
{
	assert(scene != nullptr);
	m_scene->addBaseItem(this);
	connect(&m_data, &GraphicsBaseItemData::sigPositionChanged, this, &GraphicsBaseItem::onPosChanged);
	connect(this, &QGraphicsObject::xChanged, [=] {
		m_data.setCurPos(pos(), false);
		onUpdatePinkItemPosition();
		});
	connect(this, &QGraphicsObject::yChanged, [=] {
		m_data.setCurPos(pos(), false);
		onUpdatePinkItemPosition();
		});
	connect(&m_data, &GraphicsBaseItemData::sigSizeChanged, this, &GraphicsBaseItem::onSizeChanged);
	connect(&m_data, &GraphicsBaseItemData::sigItemScaleChanged, this, &GraphicsBaseItem::onUpdatePinkItemPosition);
	connect(&m_data, &GraphicsBaseItemData::sigRotateAngleChanged, this, &GraphicsBaseItem::onRotateAngleChanged);
	setFlags(flags() | QGraphicsItem::ItemSendsGeometryChanges | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsFocusable);
	setAcceptHoverEvents(true);
}

void GraphicsBaseItem::setBorderColor(GraphicsBorderColor color)
{
	m_data.setBorderColor(GraphicsUtil::getBorderColor(color));
	update();
}

void GraphicsBaseItem::setAbsorptionLineVisable(GraphicsLineType lineType, bool isVisable)
{
	assert(m_absorptionLineItemMap[lineType]);
	m_absorptionLineItemMap[lineType]->setVisible(isVisable);
}

void GraphicsBaseItem::updateAbsorptionLinePos()
{
	assert(m_absorptionLineItemMap[GraphicsLineType::HorizontalEnum]);
	assert(m_absorptionLineItemMap[GraphicsLineType::VerticalEnum]);
	QRectF rect = m_data.getScaleRect();
	const qreal width = rect.width();
	const qreal height = rect.height();
	m_absorptionLineItemMap[GraphicsLineType::HorizontalEnum]->setLine(QLineF(0, height / 2, width, height / 2));
	m_absorptionLineItemMap[GraphicsLineType::VerticalEnum]->setLine(QLineF(width / 2, 0, width / 2, height));
}

void GraphicsBaseItem::onUpdatePinkItemPosition()
{
	for (GraphicsPinkItem* item : m_pinkItemMap.values()) {
		item->onUpdatePos();
	}
	refresh();
	for (GraphicsBaseItem* child : getItemData().getChilds()) {
		child->onUpdatePinkItemPosition();
	}
}

void GraphicsBaseItem::onRotateAngleChanged()
{
	QTransform transform = getOperation<GraphicsRotateOperation>(GraphicsOperationType::RotateOperation_Enum)->getRotateTransformOnCenter(m_data.getRotateAngle());
	setTransform(transform);
	onUpdatePinkItemPosition();
}

void GraphicsBaseItem::stretchGraphics(GraphicsPinkItemType type, const QPointF& delta)
{
	getOperation<GraphicsStretchOperation>(GraphicsOperationType::StretchOperation_Enum)->stretchGraphics(type, delta);
}

void GraphicsBaseItem::rotateGraphics(const QPointF& mouseScenePoint)
{
	getOperation<GraphicsRotateOperation>(GraphicsOperationType::RotateOperation_Enum)->setRotateAngle(mouseScenePoint);
}

void GraphicsBaseItem::stretchGraphicsOnCornerKeepRatio(GraphicsPinkItemType type, const QPointF& delta)
{
	getOperation<GraphicsStretchOperation>(GraphicsOperationType::StretchOperation_Enum)->stretchGraphicsOnCornerKeepRatio(type, delta);
}

void GraphicsBaseItem::stretchGraphicsOnCorner(GraphicsPinkItemType type, const QPointF& delta)
{
	getOperation<GraphicsStretchOperation>(GraphicsOperationType::StretchOperation_Enum)->stretchGraphicsOnCorner(type, delta);
}

void GraphicsBaseItem::scaleGraphicsOnCenter(GraphicsPinkItemType type, const QPointF& delta)
{
	getOperation<GraphicsStretchOperation>(GraphicsOperationType::StretchOperation_Enum)->stretchGraphicsOnCenter(type, delta);
}

QMap<GraphicsBaseItem*, GraphicsItemInfo> GraphicsBaseItem::getChildPressedContextMap() const
{
	QMap<GraphicsBaseItem*, GraphicsItemInfo> map;
	for (GraphicsBaseItem* child : m_data.getChilds()) {
		GraphicsItemInfo itemInfo = child->getPressedItemInfo();
		map[child] = itemInfo;
	}
	return map;
}

void GraphicsBaseItem::addChild(GraphicsBaseItem* child)
{
	child->setParentItem(this);
	m_data.addChild(child);
}

void GraphicsBaseItem::operationDone(GraphicsPinkItemType type, const QPointF& mousePoint)
{
	// 保存上下文信息
	m_data.saveContext();
	setPinksItemOpacity(1);
	// 拉伸操作
	if (type > GraphicsPinkItem::CornerEnd_Enum && type < GraphicsPinkItem::MidEnd_Enum) {
		// 记录子图元坐标
		QMap<GraphicsBaseItem*, GraphicsItemInfo> curChildsInfoMap = m_data.getChildsInfoMap();
		StretchCommand* stretchCommand = new StretchCommand(this,
			m_data.getPressItemInfo(), getChildPressedContextMap(),
			m_data.getItemInfo(), curChildsInfoMap);
		m_scene->pushCommand(stretchCommand);
	}

	// 旋转操作
	if (type == GraphicsPinkItem::Rotation_Enum) {
		RotateCommand* roateCommand = new RotateCommand(this, m_data.getPressItemInfo().angle, m_data.getRotateAngle());
		m_scene->pushCommand(roateCommand);
		m_pinkItemMap[GraphicsPinkItemType::Rotation_Enum]->setAngleVisable(false);
	}
	// 更新控制点的位置
	onUpdatePinkItemPosition();
}

void GraphicsBaseItem::operationStart(GraphicsPinkItemType type, const QPointF& mousePoint)
{
	if (m_data.getParentItem()) {
		QPointF operationStartMousePoint = m_data.getParentItem()->mapFromScene(mousePoint);
		m_data.setOperationStartMousePoint(operationStartMousePoint);
	}
	else {
		m_data.setOperationStartMousePoint(mousePoint);
	}
	// 保存上下文信息
	m_data.saveContext();

	// 对缩放图元有效
	if (type < GraphicsPinkItem::CornerEnd_Enum || type > GraphicsPinkItem::MidEnd_Enum)
		setPinksItemOpacity(0.01);

	// 旋转图元,显示角度
	if (type == GraphicsPinkItem::Rotation_Enum) {
		m_pinkItemMap[GraphicsPinkItemType::Rotation_Enum]->setOpacity(1);
		m_pinkItemMap[GraphicsPinkItemType::Rotation_Enum]->setAngleVisable(true);
	}
	m_data.savePressContext();
	setAbsorptionLineVisable(GraphicsLineType::HorizontalEnum, false);
	setAbsorptionLineVisable(GraphicsLineType::VerticalEnum, false);
}

void GraphicsBaseItem::operationDoing(GraphicsPinkItemType type, const QPointF& mousePoint)
{
	QPointF operationDoingMousePoint = m_data.getParentItem() ? m_data.getParentItem()->mapFromScene(mousePoint) : mousePoint;
	m_data.setOperationDoingMousePoint(operationDoingMousePoint);
}

void GraphicsBaseItem::enableMove(bool isCanMoveable)
{
	setFlag(QGraphicsItem::ItemIsMovable, true);
	m_data.setMovable(isCanMoveable);
}

void GraphicsBaseItem::setOriginOnCenter()
{
	m_data.setIsOriginOnCenter(true);
	const QSizeF size = m_data.getRealSize();
	QRectF boundingRect = m_data.getBoundingRect();
	boundingRect.setTopLeft(QPointF(-size.width() / 2, -size.height() / 2));
	boundingRect.setSize(size);
	m_data.setBoundingRect(boundingRect);
}

void GraphicsBaseItem::refresh()
{
	prepareGeometryChange();
}

void GraphicsBaseItem::moveToParentCenter()
{
	GraphicsBaseItem* parentItem = m_data.getParentItem();
	if (!parentItem) {
		const qreal w = m_scene->width();
		const qreal h = m_scene->height();
		const QSizeF scaleSize = m_data.getScaleSize();
		const qreal centerX = (w - scaleSize.width()) / 2.0;
		const qreal centerY = (h - scaleSize.height()) / 2.0;
		setPos(centerX, centerY);
		return;
	}

	const QSizeF size = parentItem->getItemData().getScaleSize();
	// 居中显示
	const qreal xPos = size.width() / 2.0;
	const qreal yPos = size.height() / 2.0;
	setPos(xPos, yPos);
}

void GraphicsBaseItem::addOperation(GraphicsBaseOperation* operation)
{
	m_operationMap[operation->getOperationType()] = operation;
}

void GraphicsBaseItem::addPinkItem(GraphicsPinkItem* pinkItem)
{
	m_pinkItemMap[pinkItem->getPinkItemType()] = pinkItem;
}

void GraphicsBaseItem::addAbsorptionLine(GraphicsLineItem* lineItem)
{
	m_absorptionLineItemMap[lineItem->getLineType()] = lineItem;
}

void GraphicsBaseItem::setWatermarkVisable(bool visable)
{
	assert(m_watermark);
	m_watermark->setVisible(visable);
}

void GraphicsBaseItem::onSizeChanged()
{
	emit sigSizeChanged(getItemData().getRealSize().toSize());
	onUpdatePinkItemPosition();
}

void GraphicsBaseItem::onPosChanged(const QPointF& newPos)
{
	const QPointF curPos = pos();
	if (curPos == newPos)
		return;
	blockSignals(true);
	setPos(newPos);
	blockSignals(false);
	onUpdatePinkItemPosition();
}

void GraphicsBaseItem::setPinksItemVisabled(bool isVisabled)
{
	for (GraphicsPinkItem* item : m_pinkItemMap.values()) {
		if (m_data.getExpandMode() == GraphicsItemMode::Fix_Enum &&
			(item->getPinkItemType() > GraphicsPinkItemType::CornerEnd_Enum &&
				item->getPinkItemType() < GraphicsPinkItemType::MidEnd_Enum)) {
			item->setVisible(false);
			continue;
		}
		item->setVisible(isVisabled);
	}
}

QRectF GraphicsBaseItem::boundingRect() const
{
	QRectF rect = m_data.getBoundingRect();
	QSizeF size = rect.size() * m_data.getItemScale();
	const int penWidth = m_data.getPenWidth();
	QPointF newTopleft = QPointF(-penWidth / 2, -penWidth / 2);
	if (m_data.IsOriginOnCenter()) {
		newTopleft += QPointF(-size.width() / 2, -size.height() / 2);
	}
	const QSizeF newSize(size.width() + penWidth, size.height() + penWidth);
	rect.setTopLeft(newTopleft);
	rect.setSize(newSize);
	return rect;
}

QPainterPath GraphicsBaseItem::shape() const
{
	QPainterPath path;
	path.addRect(boundingRect());
	return path;
}

void GraphicsBaseItem::hoverEnterEvent(QGraphicsSceneHoverEvent* event)
{
	m_data.setHover(true);
	setBorderColor(GraphicsUtil::Blue_Enum);
	QGraphicsObject::hoverEnterEvent(event);
}

void GraphicsBaseItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* event)
{
	setBorderColor(isSelected() ? (GraphicsUtil::Blue_Enum) : (GraphicsUtil::Cyan_Enum));
	m_data.setHover(false);
	QGraphicsObject::hoverLeaveEvent(event);
}

QVariant GraphicsBaseItem::itemChange(GraphicsItemChange change, const QVariant& value)
{
	if (GraphicsItemChange::ItemSelectedChange == change) {
		const bool isSelected = value.toBool();
		setBorderColor(isSelected ? (GraphicsUtil::Blue_Enum) : (GraphicsUtil::Cyan_Enum));
		setPinksItemVisabled(isSelected);
	}
	else if (GraphicsItemChange::ItemPositionChange == change && m_data.isMovable()) {
		if (!m_data.getParentItem()) {
			m_data.setCurPos(value.toPointF(), false);
			return QGraphicsItem::itemChange(change, value);
		}
		return getOperation<GraphicsMoveOperation>(GraphicsOperationType::MoveOperation_Enum)->onMoveEvent(value.toPointF());
	}
	return QGraphicsItem::itemChange(change, value);
}

void GraphicsBaseItem::mousePressEvent(QGraphicsSceneMouseEvent* event)
{
	if (event->button() != Qt::LeftButton)
		return;

	if (m_data.isMovable()) {
		setCursor(Qt::SizeAllCursor);
		// 记录按下位置
		m_data.savePressContext();
	}

	setFocus();
	m_scene->setSelectItem(this);
	QGraphicsObject::mousePressEvent(event);
}

void GraphicsBaseItem::mouseReleaseEvent(QGraphicsSceneMouseEvent* event)
{
	if (m_data.isMovable()) {
		unsetCursor();
		const QPointF curPos = pos();
		const QPointF deltaPos = curPos - m_data.getPressItemInfo().pos;
		if (std::abs(deltaPos.x()) > 1 || std::abs(deltaPos.y()) > 1) {
			// 将移动命令压入堆栈
			MoveCommand* moveCommand = new MoveCommand(this, m_data.getPressItemInfo().pos, curPos);
			m_scene->pushCommand(moveCommand);
		}
	}
	m_data.setIsMoving(false);
	if (m_data.getParentItem()) {
		m_data.getParentItem()->setAbsorptionLineVisable(GraphicsLineType::HorizontalEnum, false);
		m_data.getParentItem()->setAbsorptionLineVisable(GraphicsLineType::VerticalEnum, false);
	}
	clearFocus();
	setPinksItemVisabled(true);
	QGraphicsObject::mouseReleaseEvent(event);
}

void GraphicsBaseItem::mouseMoveEvent(QGraphicsSceneMouseEvent* event)
{
	if (!m_data.isMovable()) {
		QGraphicsObject::mouseMoveEvent(event);
		return;
	}
	if (event->buttons() != Qt::LeftButton)
		return;
	m_data.setIsMoving(true);
	setPinksItemVisabled(false);
	QGraphicsObject::mouseMoveEvent(event);
}

void GraphicsBaseItem::createWatermark()
{
	m_watermark = new GraphicsWatermarkItem(this);
	connect(&m_data, &GraphicsBaseItemData::sigItemScaleChanged, this, &GraphicsBaseItem::onUpdateWatermarkGeometry);
	connect(&m_data, &GraphicsBaseItemData::sigSizeChanged, this, &GraphicsBaseItem::onUpdateWatermarkGeometry);
}

void GraphicsBaseItem::onUpdateWatermarkGeometry()
{
	const QSizeF scaleSize = m_data.getScaleSize();
	if (getItemData().IsOriginOnCenter()) {
		m_watermark->setRect(QRectF(-scaleSize.width() / 2.0, -scaleSize.height() / 2.0, scaleSize.width(), scaleSize.height()));
	}
	else {
		m_watermark->setRect(QRectF(0, 0, scaleSize.width(), scaleSize.height()));
	}
}

void GraphicsBaseItem::setPinksItemOpacity(qreal opacity)
{
	for (GraphicsPinkItemType key : m_pinkItemMap.keys()) {
		m_pinkItemMap[key]->setOpacity(opacity);
	}
}